/* 
  BASIC INTERRUPT DRIVEN DRIVER FOR SPI. 
*/

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

/* Demo application includes. */
#include "Board.h"
//#include "lib_AT91SAM7X256Lite.h"
#include "SSD1306Spi.h"

/*-----------------------------------------------------------*/

/* The ISR can cause a context switch so is declared naked. */
void vSpiISREntry0( void ) __attribute__ ((naked));
void vSpiISREntry1( void ) __attribute__ ((naked));

/* The function that actually performs the ISR work.  This must be separate
from the wrapper function to ensure the correct stack frame gets set up. */
void vSpiISR0( void );
void vSpiISR1( void );
/*-----------------------------------------------------------*/

void vSpiISR0(void)
{    
	portBASE_TYPE xTaskWokenBySPI = pdFALSE;
	unsigned portLONG ulStatus;

	/* Original cause of the interruption */
	ulStatus = AT91C_BASE_SPI0->SPI_SR &= AT91C_BASE_SPI0->SPI_IMR;

	/*Is a transmit interrupt*/
	if (ulStatus & (AT91C_SPI_RXBUFF | AT91C_SPI_TXEMPTY))
	{
		AT91F_SPI_DisableIt(AT91C_BASE_SPI0, AT91C_SPI_TXEMPTY | AT91C_SPI_RXBUFF);
		/*Signal semaphore*/
		xSemaphoreGiveFromISR(spiPort0.event.xSemaphore, &xTaskWokenBySPI);
	}

	/* End the interrupt in the AIC. */
	AT91C_BASE_AIC->AIC_EOICR = 0;

	/* If a task was woken by either a character being received or a character
	 being transmitted then we may need to switch to another task. */
	//portEND_SWITCHING_ISR(xTaskWokenBySPI);
	/* Do a task switch if needed */
	if( xTaskWokenBySPI )
	{
		/* This call will ensure that the unblocked task will be executed
		immediately upon completion of the ISR if it has a priority higher
		than the interrupted task. */
		portYIELD_FROM_ISR();
	}
}

void vSpiISR1(void)
{    
	portBASE_TYPE xTaskWokenBySPI = pdFALSE;
	unsigned portLONG ulStatus;

	/* Original cause of the interruption */
	ulStatus = AT91C_BASE_SPI1->SPI_SR &= AT91C_BASE_SPI1->SPI_IMR;

	/*Is a transmit interrupt*/
	if (ulStatus & (AT91C_SPI_RXBUFF | AT91C_SPI_TXEMPTY))
	{
		AT91F_SPI_DisableIt(AT91C_BASE_SPI1, AT91C_SPI_TXEMPTY | AT91C_SPI_RXBUFF);
		/*Signal semaphore*/
		xSemaphoreGiveFromISR(spiPort1.event.xSemaphore, &xTaskWokenBySPI);
	}

	/* End the interrupt in the AIC. */
	AT91C_BASE_AIC->AIC_EOICR = 0;

	/* If a task was woken by either a character being received or a character
	 being transmitted then we may need to switch to another task. */
	//portEND_SWITCHING_ISR(xTaskWokenBySPI);
	/* Do a task switch if needed */
	if( xTaskWokenBySPI )
	{
		/* This call will ensure that the unblocked task will be executed
		immediately upon completion of the ISR if it has a priority higher
		than the interrupted task. */
		portYIELD_FROM_ISR();
	}
}

/*-----------------------------------------------------------*/

void vSpiISREntry0( void )
{
	/* Save the context of the interrupted task. */
	portSAVE_CONTEXT();

	/* Call the handler to do the work.  This must be a separate
	function to ensure the stack frame is set up correctly. */
	vSpiISR0();

	/* Restore the context of whichever task will execute next. */
	portRESTORE_CONTEXT();
}

void vSpiISREntry1( void )
{
	/* Save the context of the interrupted task. */
	portSAVE_CONTEXT();

	/* Call the handler to do the work.  This must be a separate
	function to ensure the stack frame is set up correctly. */
	vSpiISR1();

	/* Restore the context of whichever task will execute next. */
	portRESTORE_CONTEXT();
}

